
#include "lexer.hpp"

Lexer::Lexer(){}

Lexer::~Lexer(){
	fclose(file);
	fclose(output);
}

void Lexer::add_keywords() {
    const char* src = "SELECT FROM WHERE AS INSERT INTO VALUES VALUE DEFAULT\
                UPDATE SET DELETE JOIN LEFT RIGHT ON MIN MAX AVG SUM \
                UNION ALL GROUP BY HAVING DISTINCT ORDER BY TRUE FALSE UNKNOWN \
                IS NULL AND OR XOR NOT";
    this->src = (char*)src;
    size_t keyword_size = sizeof(keywords)/sizeof(keywords[0]);
    for(size_t i = 0; i < keyword_size; i++) {
        this->next(0);
        this->symtab[this->symtab.size() - 1].type = keywords[i];
    }
    
}

void Lexer::init(string output_file) {
    std::vector<std::string> keywords = {
        "SELECT", "FROM", "WHERE", "AS", "INSERT", "INTO", "VALUES", "VALUE", "DEFAULT",
        "UPDATE", "SET", "DELETE", "JOIN", "LEFT", "RIGHT", "ON", "MIN", "MAX", "AVG", "SUM",
        "UNION", "ALL", "GROUP BY", "HAVING", "DISTINCT", "ORDER BY", "TRUE", "FALSE", "UNKNOWN",
        "IS", "NULL", "AND", "OR", "XOR", "NOT"
    };
	output = fopen(output_file.c_str(), "w");
	if(!output)
	{
		printf("lexer output file open fail\n");
	}
    this->keywords_name = keywords;
    this->add_keywords();
}

// 向前查看 n 个字符, 用于对于有相同部分的标识符进行识别
char* Lexer::lookupn(int size) {
    char* ptr = src - size;
    char* buf = new char[size + 5];
    memcpy(buf, ptr, size);
    buf[size] = '\0';
    return buf;
}

char* Lexer::lookdown(int size) {
    char* buf = new char[size + 5];
    memcpy(buf, src, size);
    buf[size] = '\0';
    return buf;
}

void Lexer::add_idn_to_token(Symbol symbol) {
    if(std::find(this->keywords_name.begin(), this->keywords_name.end(), symbol.name) != this->keywords_name.end()) {
        // 发现关键字, 直接将关键字的名字加入到 parser_token type 中
        this->parser_token.type = symbol.name;
    }else {
        // 此时为标识符
        this->parser_token.type = "IDN";
        this->parser_token.str.emplace(symbol.name);
        this->parser_token.value.emplace(symbol.value);
    }
}

// 获取下一个 token
bool Lexer::next(bool show) {
    char* last_pos;
    while((token = *src) && *src != 0) {
		selectBuffer(1);
        if((token >= 'a' && token <= 'z') || (token >= 'A' && token <= 'Z') || token == '_') {
            last_pos = src - 1;
			int last_pos = 1;
            // 获取符号名
            char nameBuffer[100];
			memset(nameBuffer, 0, 100);
            nameBuffer[0] = token;
            while((*src >= 'a' && *src <= 'z') || (*src >= 'A' && *src <= 'Z') || (*src >= '0' && *src <= '9') || *src == '_') {
                nameBuffer[last_pos] = *src;
				last_pos++;
				selectBuffer(1);
            }
            nameBuffer[last_pos] = 0;
            if(strcmp(nameBuffer, "ORDER") == 0 || strcmp(nameBuffer, "GROUP") == 0) {
                char* buf = this->lookdown(3);
                if(strcmp(buf, " BY") == 0) {
                    // 将指针向后移 3 位
					selectBuffer(3);
                    memcpy(nameBuffer + last_pos, buf, 3);
                }
            }
            // 从符号表中查找是否有对应的符号名
            for(auto sym: this->symtab) {
                if(strcmp(sym.name, nameBuffer) == 0) {
                    this->token_val.sym_ptr = &sym;
                    this->token_type = sym.type;
                    char* name = new char[MAX_NAME_SIZE];
                    strcpy(name, nameBuffer);
                    this->name = std::make_optional(name);
                    // 将标识符添加到 parser_token 中
                    this->add_idn_to_token(sym);
                    goto OUT;
                }
            }
#ifdef DEBUG
            printf("[Debug] next(): name: %s\n", nameBuffer);
#endif
            // 如果未发现的话则需要构建符号
            Symbol symbol;
            strcpy(symbol.name, nameBuffer);
            symbol.type = TokenType::Idn;
            symbol.value = 0.0;
            this->symtab.push_back(symbol);
            this->token_val.sym_ptr = &this->symtab[this->symtab.size() - 1];
            this->token_type = TokenType::Idn;
            char* name = new char[MAX_NAME_SIZE];
            strcpy(name, nameBuffer);
            this->name = std::make_optional(name);
            // 将标识符加入到 parser_token 中
            this->add_idn_to_token(symbol);
            goto OUT;
        }else if(token >= '0' && token <= '9') {
            this->token_val.value = (double)token - '0';
            while(*src >= '0' && *src <= '9') {
                this->token_val.value = this->token_val.value * 10.0 + ((double)(*src) - '0');
				selectBuffer(1);
            }
            if(*src == '.') {
                this->token_type = TokenType::Float;
                // 浮点数
				selectBuffer(1);
                int countDig = 1;
                while(*src >= '0' && *src <= '9') {
                    this->token_val.value = this->token_val.value + ((double)(*src) - '0')/(10.0 * countDig);
					selectBuffer(1);
                    countDig++;
                }
                this->parser_token.type = "FLOAT";
            }else {
                this->token_type = TokenType::Int;
                this->parser_token.type = "INT";
            }
            this->parser_token.value.emplace(this->token_val.value);
            goto OUT;
        }else if(token == '"') {
            // 字符串
            int size = 0;
            while(*src != token) {
                if(*src == 0) {
                    this->token_type = TokenType::Invalid;
                    goto OUT;
                }
				selectBuffer(1);
                size++;
            }
            // 将对应的字符串放入地址中并将其存入符号表中
            char* str = new char[size + 5];
            memcpy(str, src - size, size);
            str[size] = '\0';
            this->token_type = TokenType::Str;
            this->token_val.str_ptr = str;
			selectBuffer(1);
            // 为 parser 添加 token
            this->parser_token.type = "STRING";
            this->parser_token.str.emplace(str);
            goto OUT;
        }else if(token == '*'){
            this->parser_token.type = "*";
            this->token_type = TokenType::WildCard;
            goto OUT;
        }else if(token == '='){
            // 等于符号
            this->parser_token.type = "=";
            this->token_type = TokenType::Equal;
            goto OUT;
        }else if(token == '!'){
            // 不等于符号
            if(*src == '=') {
				selectBuffer(1);
                this->parser_token.type = "!=";
                this->token_type = TokenType::NonEqual;
            }else{
                this->parser_token.type = "!";
                this->token_type = TokenType::Not2;
            }
            goto OUT;
        }else if(token == '<') {
            // < / <= / <=>
            if(*src == '=') {
				selectBuffer(1);
                if(*src == '>') {
					selectBuffer(1);
                    this->parser_token.type = "<=>";
                    this->token_type = TokenType::SafeEqual;
                }else {
                    this->parser_token.type = "<=";
                    this->token_type = TokenType::LessEqual;
                }
            }else {
                this->parser_token.type = "<";
                this->token_type = TokenType::Less;
            }
            goto OUT;
        }else if(token == '>') {
            // > / >=
            if(*src == '=') {
				selectBuffer(1);
                this->parser_token.type = ">=";
                this->token_type = TokenType::GreatEqual;
            }else {
                this->parser_token.type = ">";
                this->token_type = TokenType::Great;
            }
            goto OUT;
        }else if(token == '|') {
            if(*src == '|') {
				selectBuffer(1);
                this->parser_token.type = "||";
                this->token_type = TokenType::Or2;
            }else {
                this->parser_token.type = "INVALID";
                this->token_type = TokenType::Invalid;
            }
            goto OUT;
        }else if(token == '&') {
            if(*src == '&') {
				selectBuffer(1);
                this->parser_token.type = "&&";
                this->token_type = TokenType::And2;
            }else{
                this->parser_token.type = "INVALID";
                this->token_type = TokenType::Invalid;
            }
            goto OUT;
        }else if(token == '-'){
            this->parser_token.type = "-";
            this->token_type = TokenType::Sub;
            goto OUT;
        }else if(token == '.'){
            this->parser_token.type = ".";
            this->token_type = TokenType::Dot;
            goto OUT;
        }else if(token == '('){
            this->parser_token.type = "(";
            this->token_type = TokenType::Lp;
            goto OUT;
        }else if(token == ')'){
            this->parser_token.type = ")";
            this->token_type = TokenType::Rp;
            goto OUT;
        }else if(this->token == ','){
            this->parser_token.type = ",";
            this->token_type = TokenType::Comma;
            goto OUT;
        }
		else if(this->token == ';'){
			this->parser_token.type = "VAREPSILON";
			this->token_type = TokenType::END;
			goto OUT;
		}else if(token == ' ' || token == '\t' || token == '\n') {}
        else if(token == '\\'){}
		else if(token == '/')
		{
			if(*src == '/')
			{
				selectBuffer(1);
				while(*src != '\n')
				{
					selectBuffer(1);
				}
			}
		}
        else{
            this->parser_token.type = "INVALID";
            this->token_type = TokenType::Invalid;
        }
    }
OUT:;
	if(show)
	{
		return printToken();
	}
	return true;
}

Token Lexer::get_token() {
    return this->parser_token;
}

int Lexer::get_current_token_value() {
    return this->token;
}

// 运行
void Lexer::run(string file_name) {
	current_len = 0;
    file = fopen(file_name.c_str(), "rb");
	if(!file)
	{
		cout << "file can't open" << endl;
		return ;
	}
	end = false;
	memset(buffer, 0, 1025);
	fread(buffer, 1, 1024, file);
	if(feof(file))
		end = true;
	src = buffer;
}

bool Lexer::printToken()
{
	if(this->token > 0) {
        switch(this->token_type) {
            case TokenType::Float: 
                fprintf(output,"FLOAT\t<FLOAT,%lf>\n", this->token_val.value);
                break;
            case TokenType::Int:
                fprintf(output,"INT\t<INT,%d>\n", (int)this->token_val.value);
                break;
            case TokenType::Str:
                fprintf(output, "STRING\t<STRING,%s>\n", (char*)this->token_val.str_ptr);
                // fprintf(output, "%s\n", (char*)this->token_val.str_ptr);
                break;
            case TokenType::Idn:
                if(this->name.has_value()) {
                    fprintf(output, "%s\t<IDN,%s>\n", this->name.value(), this->name.value());
                }
                break;
            case TokenType::Lp:
                fprintf(output, "(\t<SE,1>\n");
                break;
            case TokenType::Rp:
                fprintf(output, ")\t<SE,2>\n");
                break;
            case TokenType::Comma:
                fprintf(output, ",\t<SE,3>\n");
                break;
            case TokenType::Equal:
                fprintf(output, "=\t<OP,1>\n");
                break;
            case TokenType::Great:
                fprintf(output, ">\t<OP,2>\n");
                break;
            case TokenType::Less:
                fprintf(output, "<\t<OP,3>\n");
                break;
            case TokenType::GreatEqual:
                fprintf(output, ">=\t<OP,4>\n");
                break;
            case TokenType::LessEqual:
                fprintf(output, "<=\t<OP,5>\n");
                break;
            case TokenType::NonEqual:
                fprintf(output, "!=\t<OP,6>\n");
                break; 
            case TokenType::SafeEqual:
                fprintf(output, "<=>\t<OP,7>\n");
                break;
            case TokenType::And:
                fprintf(output, "AND\t<OP,8>\n");
                break;
            case TokenType::And2:
                fprintf(output, "&&\t<OP,9>\n");
                break;
            case TokenType::Or:
                fprintf(output, "OR\t<OP,10>\n");
                break;
            case TokenType::Or2:
                fprintf(output, "||\t<OP,11>\n");
                break;
            case TokenType::Xor:
                fprintf(output, "XOR\t<OP,12>\n");
                break;
            case TokenType::Not:
                fprintf(output, "NOT\t<OP,13>\n");
                break;
            case TokenType::Not2:
                fprintf(output, "!\t<OP,14>\n");
                break;
            case TokenType::Sub:
                fprintf(output, "-\t<OP,15>\n");
                break;
            case TokenType::Dot:
                fprintf(output, ".\t<OP,16>\n");
                break;
            case TokenType::Select:
                fprintf(output, "SELECT\t<KW,1>\n");
                break; 
            case TokenType::From:
                fprintf(output, "FROM\t<KW,2>\n");
                break;
            case TokenType::Where:
                fprintf(output, "WHERE\t<KW,3>\n");
                break;
            case TokenType::As:
                fprintf(output, "AS\t<KW,4>\n");
                break;
            case TokenType::WildCard:
                fprintf(output, "*\t<KW,5>\n");
                break;
            case TokenType::Insert:
                fprintf(output, "INSERT\t<KW,6>\n");
                break;
            case TokenType::Into:
                fprintf(output, "INTO\t<KW,7>\n");
                break;
            case TokenType::Values:
                fprintf(output, "VALUES\t<KW,8>\n");
                break;
            case TokenType::Value:
                fprintf(output, "VALUE\t<KW,9>\n");
                break;
            case TokenType::Default:
                fprintf(output, "DEFAULT\t<KW,10>\n");
                break;
            case TokenType::Update:
                fprintf(output, "UPDATE\t<KW,11>\n");
                break;
            case TokenType::Set:
                fprintf(output, "SET\t<KW,12>\n");
                break;
            case TokenType::Delete:
                fprintf(output, "DELETE\t<KW,13>\n");
                break;
            case TokenType::Join:
                fprintf(output, "JOIN\t<KW,14>\n");
                break;
            case TokenType::Left:
                fprintf(output, "LEFT\t<KW,15>\n");
                break;
            case TokenType::Right:
                fprintf(output, "RIGHT\t<KW,16>\n");
                break;
            case TokenType::On:
                fprintf(output, "ON\t<KW,17>\n");
                break;
            case TokenType::Min:
                fprintf(output, "MIN\t<KW,18>\n");
                break;
            case TokenType::Max:
                fprintf(output, "MAX\t<KW,19>\n");
                break;
            case TokenType::Avg:
                fprintf(output, "AVG\t<KW,20>\n");
                break;
            case TokenType::Sum:
                fprintf(output, "SUM\t<KW,21>\n");
                break;
            case TokenType::Union:
                fprintf(output, "UNION\t<KW,22>\n");
                break;
            case TokenType::All:
                fprintf(output, "ALL\t<KW,23>\n");
                break;
            case TokenType::GroupBy:
                fprintf(output, "GROUP BY\t<KW,24>\n");
                break;
            case TokenType::Having:
                fprintf(output, "HAVING\t<KW,25>\n");
                break;
            case TokenType::Distinct:
                fprintf(output, "DISTINCT\t<KW,26>\n");
                break;
            case TokenType::OrderBy:
                fprintf(output, "ORDER BY\t<KW,27>\n");
                break;
            case TokenType::True:
                fprintf(output, "TRUE\t<KW,28>\n");
                break;
            case TokenType::False:
                fprintf(output, "FALSE\t<KW,29>\n");
                break;
            case TokenType::Unknown:
                fprintf(output, "UNKNOWN\t<KW,30>\n");
                break;
            case TokenType::Is:
                fprintf(output, "IS\t<KW,31>\n");
                break;
            case TokenType::Null:
                fprintf(output, "NULL\t<KW,32>\n");
                break;
			case TokenType::END:break;
            case TokenType::Invalid:
                printf("[Error] Unexpected token: %c\n", token);
                return false;
            default:
                printf("[Error] Fail to find token\n");
        }
#ifdef DEBUG
        printf("[Debug] parser: ");
        printf("type: %s ", this->parser_token.type.c_str());
        if(this->parser_token.value.has_value()) {
            printf("value: %lf ", this->parser_token.value.value());
        }
        if(this->parser_token.str.has_value()) {
            printf("str: %s\n", this->parser_token.str.value().c_str());
        }
#endif
	return true;
    }
	return false;
} 

void Lexer::selectBuffer(int len)
{
	if(current_len + len >= 1024)
	{
		if(end)
		{
			current_len = 1024;
			src = buffer + 1024;
			return;
		}
		current_len = current_len + len - 1024;
		memset(buffer, 0, 1025);
		fread(buffer, 1, 1024, file);
		end = feof(file);
		src = buffer + current_len;
	}
	else
	{
		current_len = current_len + len;
		src += len;
	}
}
